#ifndef MYWAVEFORMCOMPONENT_H
#define MYWAVEFORMCOMPONENT_H


// Some Listeners //

class MyWaveformMultiDisplayListener
{
public:
    virtual ~MyWaveformMultiDisplayListener() {}
    virtual void samplesPerPixelChanged (class MyWaveformMultiDisplay* waveform) { }
	virtual void samplesOffsetChanged (class MyWaveformMultiDisplay* waveform) { }
	virtual void verticalZoomChanged (class MyWaveformMultiDisplay* waveform) { }
};

class MyWaveformMarkerHandleListener
{
public:
    virtual ~MyWaveformMarkerHandleListener() {}
    virtual void positionChanged (class MyWaveformMarkerHandle* marker) { }
};

class MyWaveformSelectionListener
{
public:
    virtual ~MyWaveformSelectionListener() {}
    virtual void selectionChanged (class MyWaveformSelection* selection) { }
};

class MyWaveformEditorListener :	public MyWaveformSelectionListener,
									public MyWaveformMultiDisplayListener,
									public MyWaveformMarkerHandleListener
{
public:
    virtual ~MyWaveformEditorListener() {}
};




/**
 A horizontal (time) ruler for waveforms.

 This should only be a child component of MyWaveformMultiDisplay
*/
class MyWaveformHorizontalRuler : public Component
{
public:
	MyWaveformHorizontalRuler(bool isAbove_);
	void paint(Graphics& g);
	
	int chooseBestDivisionSize(double samplesPerPixel);
	
	class MyWaveformMultiDisplay* getParentComponent() { return (MyWaveformMultiDisplay*)Component::getParentComponent(); }
	
private:
	bool isAbove;
};

/**
 A vertical (level) ruler for waveforms.
 
 This should only be a child component of MyWaveformChannelDisplay
*/
class MyWaveformVerticalRuler : public Component
{
public:
	MyWaveformVerticalRuler(bool isLeft_);
	void paint(Graphics& g);
	
	class MyWaveformChannelDisplay* getParentComponent() { return (MyWaveformChannelDisplay*)Component::getParentComponent(); }

private:
	bool isLeft;
};


enum WaveformDisplayStyle {
	Normal = 0,
	Blocky, 
	RoundBlocky, 
	NormalAndRoundBlocky
};

class MyWaveformChannelDisplay : public Component
{
public:
	MyWaveformChannelDisplay(WaveformDisplayStyle style_, bool leftRuler, bool rightRuler);
	~MyWaveformChannelDisplay();
	
	class MyWaveformMultiDisplay* getParentComponent() { return (MyWaveformMultiDisplay*)Component::getParentComponent(); }
	
	double getVerticalZoom();
	
	void paint(Graphics& g);
	void paintNormal(Graphics& g);
	void paintBlocky(Graphics& g);
	void paintRoundBlocky(Graphics& g);
	void resized();
	
	int getWaveformWidth();
	void allocateBuffer();
	
	int* getBufferMin() { return bufferMin; }
	int* getBufferMax() { return bufferMax; }
	
	friend class MyWaveformMultiDisplay;
	
private:		
	CriticalSection lock;
	
	WaveformDisplayStyle style;
		
	int* bufferMin;
	int* bufferMax;
		
	MyWaveformVerticalRuler*	verticalRulerLeft;
	MyWaveformVerticalRuler*	verticalRulerRight;
};


class MyWaveformMultiDisplay : public Component
{
public:
	
	MyWaveformMultiDisplay(WaveformDisplayStyle style_, bool rulerLeft, bool rulerRight, bool rulerAbove, bool rulerBelow);
	~MyWaveformMultiDisplay();
	
	
	
	void addListener (MyWaveformMultiDisplayListener* const listener);
	void removeListener (MyWaveformMultiDisplayListener* const listener);
	void sendSamplesPerPixelChanged();
	void sendSamplesOffsetChanged();
	void sendVerticalZoomChanged();
	//void handleAsyncUpdate();
	
	
	class MyWaveformEditor* getParentComponent() { return (MyWaveformEditor*)Component::getParentComponent(); }
	
	void paint(Graphics& g);
	void resized();
	void resizeChildren();
	
	const Rectangle getWaveformBounds();
	
	void setReader(AudioFormatReader* reader_);
	void fillBuffers();
	
	void setSamplesPerPixel(double newSamplesPerPixel);
	void setSamplesOffset(double newSamplesOffset);
	void setVerticalZoom(double newVerticalZoom);
	
	double getVerticalZoom()		{ return verticalZoom; }
	double getSamplesOffset()		{ return samplesOffset; }
	int getQuantisedSamplesOffset();
	double getSamplesPerPixel()		{ return samplesPerPixel; }
	
	int samplesToPixels(int samples);
	int pixelsToSamples(int pixels);
	
	int limitSamplesToReader(int samplesToLimit);
	int snapToReadersZeroCrossings(const int samplesToSnap, const int tempBufferSize = 512);
	int getReaderNumSamples();
	
private:
	SortedSet <void*> listeners;
	CriticalSection lock;
	
	WaveformDisplayStyle style;
	
	AudioFormatReader* reader; 
	Array<MyWaveformChannelDisplay*> channelDisplays;
	
	double samplesPerPixel, samplesOffset, verticalZoom;
	
	bool hasRulerLeft, hasRulerRight, hasRulerAbove, hasRulerBelow;
	MyWaveformHorizontalRuler*	horizontalRulerAbove;
	MyWaveformHorizontalRuler*	horizontalRulerBelow;
};


class MyWaveformMarkerLabel :  public Label
{
public:
	MyWaveformMarkerLabel(String name);
		
	void setWidth(int width) { setSize(width, getHeight()); }
};

class MyWaveformMarkerHandle :	public Component
{
public:
	MyWaveformMarkerHandle(const String& name, class MyWaveformEditor* editor_, class MyWaveformSelection* selection_ = 0);
	~MyWaveformMarkerHandle();
	
	void addListener (MyWaveformMarkerHandleListener* const listener);
	void removeListener (MyWaveformMarkerHandleListener* const listener);
	void sendPositionChanged();
	//void handleAsyncUpdate();
	
	void visibilityChanged();
	
	void paint(Graphics& g);
	void moved();
	void resized();
	
	void mouseDown(const MouseEvent& e);
    void mouseDrag(const MouseEvent& e);
	void mouseUp(const MouseEvent& e);
	
	void setMousePositionToThisHandle();
	
	void setColour(const Colour& newColour);
	
	int samplesToPixels(int samples);
	int pixelsToSamples(int pixels);
	int limitSamplesToReader(int samplesToLimit);
	int snapToReadersZeroCrossings(int samplesToSnap);
	int getReaderNumSamples();
	
	double getSamplesPerPixel();
	const Rectangle getWaveformDisplayBounds();
	
	void updatePosition();
	void setPosition(int newSamplePosition, bool shouldUpdatePosition = true, bool shouldDisableZeroCrossingSnap = false);
	int getPosition() { return samplePosition; }
	
	const String getLabelName() { 
		return markerLabel->getText(); 
	}
	
	void setLabelName(const String& name) 
	{ 
		markerLabel->setName(String(name)<<" Label");
		markerLabel->setText(name,true);
	}
	
	void setShouldDisplayLabelWhenVisible(bool flag);
	
	friend class MyWaveformSelection;
	
private:
	SortedSet <void*> listeners;
	CriticalSection lock;
	
	class MyWaveformEditor*				waveformEditor;
	class MyWaveformSelection*			selection;
	MyWaveformMarkerLabel*				markerLabel;
	bool								shouldDisplayLabelWhenVisible;
	int									markerId;
	MyWaveformMarkerHandle*				draggingHandle;
	Colour								colour;
	
	bool								snapToZeroCrossings;

	
	int originalX;
	int samplePosition;
	
	int mouseDownScreenX;
	int mouseDownScreenY;
	
	bool ctrlDownLastDrag;
};



class MyWaveformSelection : public Component,
							private MyWaveformMarkerHandleListener
{
public:
	MyWaveformSelection(MyWaveformEditor* editor_);
	~MyWaveformSelection();
	
	void addListener (MyWaveformSelectionListener* const listener);
	void removeListener (MyWaveformSelectionListener* const listener);
	void sendSelectionChanged();
	//void handleAsyncUpdate();
	void positionChanged (MyWaveformMarkerHandle* marker);
	
	class MyWaveformEditor* getParentComponent() { return (MyWaveformEditor*)Component::getParentComponent(); }
	
	void visibilityChanged();
	
	void paint(Graphics& g);
	void resized();
	
	void mouseDown(const MouseEvent& e);
    void mouseDrag(const MouseEvent& e);
	void mouseUp(const MouseEvent& e);
	
	void setColour(const Colour& newColour);
	
	MyWaveformMarkerHandle* getStartSelectionMarker()	{ return startSelectionMarker; }
	MyWaveformMarkerHandle* getEndSelectionMarker()		{ return endSelectionMarker; }
	
	int samplesToPixels(int samples);
	int pixelsToSamples(int pixels);
	int limitSamplesToReader(int samplesToLimit);
	int snapToReadersZeroCrossings(int samplesToSnap);
	int getReaderNumSamples();
	
	double getSamplesPerPixel();
	const Rectangle getWaveformDisplayBounds();

	
	void updatePositions();
	void swapMarkers();
	
	void setPositions(int start, int end);
	bool isUpdatingPositions() { return updatingPositions; }
	
	
private:
	SortedSet <void*> listeners;
	CriticalSection lock;
	
	class MyWaveformEditor*		waveformEditor;
	MyWaveformMarkerHandle*		startSelectionMarker;
	MyWaveformMarkerHandle*		endSelectionMarker;
	MyWaveformMarkerHandle*		draggingHandle;
	Colour						colour;
	bool						updatingPositions;
	bool						displayMarkersAndSelection;
};





class MyWaveformEditor : public Component
{
public:
	MyWaveformEditor(WaveformDisplayStyle style_, bool rulerLeft, bool rulerRight, bool rulerAbove, bool rulerBelow);
	~MyWaveformEditor();
	
	void addListener (MyWaveformEditorListener* const listener);
	void removeListener (MyWaveformEditorListener* const listener);
	//void handleAsyncUpdate();
	
	void paint(Graphics& g);
	void resized();

	void setReader(AudioFormatReader* reader_);
	void setSamplesPerPixel(double newSamplesPerPixel);
	void setSamplesOffset(double newSamplesOffset);
	void setVerticalZoom(double newVerticalZoom);
	
	int samplesToPixels(int samples);
	int pixelsToSamples(int pixels);
	int limitSamplesToReader(int samplesToLimit);
	int snapToReadersZeroCrossings(int samplesToSnap);
	int getReaderNumSamples();
	
	double getSamplesPerPixel();
	MyWaveformMultiDisplay* getWaveformDisplay() { return waveformDisplay; } 
	const Rectangle getWaveformDisplayBounds();
	
	MyWaveformSelection* getSelection() { return selection; }
	void getSelection(int& start, int& end);
	void setSelection(int start, int end);
	
	int getHighestMarkerId();
	int getNextMarkerId();
	
private:
	SortedSet <void*> listeners;
	CriticalSection lock;
	
	WaveformDisplayStyle style;
	
	MyWaveformMultiDisplay*		waveformDisplay;
	ResizableCornerComponent*	resizer;
	ComponentBoundsConstrainer  sizeLimits;
	
	MyWaveformSelection*		selection;
		
	int markerIdCount;
};


#endif // MYWAVEFORMCOMPONENT_H

